import { cloneDeep, debounce as deb } from 'lodash-es'
import { isRef } from 'vue'
import BigNumber from 'bignumber.js'

/**
 * 通用js方法封装处理
 */

/**
 * 处理 excel 导出字典数据翻译
 * @param dataList
 * @param dictKeyValueField
 * @param dictDot
 */
export function handleExcelDictData(dataList, dictKeyValueField, dictDot = '、') {
  const _dataList = cloneDeep(dataList)
  let needConvertFields
  if (dictKeyValueField) {
    needConvertFields = Object.keys(dictKeyValueField)
  }
  if (_dataList.length) {
    _dataList.forEach((row) => {
      for (const rowKey in row) {
        if (needConvertFields.includes(rowKey)) {
          // 该字段需要转换
          let _valueList
          if (!Array.isArray(row[rowKey])) {
            if (typeof row[rowKey] === 'string' && row[rowKey].indexOf(',') !== -1) {
              _valueList = row[rowKey].split(',')
            } else {
              _valueList = [row[rowKey]]
            }
          } else {
            _valueList = row[rowKey]
          }
          let newValue = ''
          _valueList.forEach((value, index) => {
            if (index < _valueList.length - 1) {
              if (dictKeyValueField[rowKey][value]) {
                // 找到字典翻译
                newValue += `${dictKeyValueField[rowKey][value]}${dictDot}`
              } else {
                // 没找到字典 显示值
                newValue += `${value}${dictDot}`
              }
            } else {
              if (dictKeyValueField[rowKey][value]) {
                newValue += `${dictKeyValueField[rowKey][value]}`
              } else {
                newValue += `${value}`
              }
            }
          })
          row[rowKey] = newValue
        }
      }
    })
    return _dataList
  } else {
    return []
  }
}

/**
 * 首字母大写
 * @param str
 * @returns {string}
 */
export function toUpperCaseFirst(str) {
  if (str) {
    return str.charAt(0).toUpperCase() + str.slice(1)
  } else {
    return ''
  }
}

/**
 * 前 num 天的日期时间
 * @param num
 * @returns {Date}
 */
export function beforeDayDate(num) {
  const currentDate = new Date() // 获取当前日期
  currentDate.setDate(currentDate.getDate() - num) // 将当前日期往前推7天
  return currentDate
}

/**
 * 获取某年某月一共多少天
 * @param year
 * @param month
 * @returns {number}
 */
export function getDaysInMonth(year, month) {
  return new Date(year, month, 0).getDate()
}

/**
 * 防抖 统一 lodash debounce 属性
 * @param {Function} func - 要防抖动的函数
 * @param {Number} [wait] - 需要延迟的毫秒数
 * @param {Object} [options] - 选项对象
 * @param {Boolean} [options.leading] - 指定在延迟开始前调用
 * @param {Number} [options.maxWait] - 设置 func 允许被延迟的最大值
 * @param {Boolean} [options.trailing] - 指定在延迟结束后调用
 * @returns {DebouncedFuncLeading<*> | DebouncedFunc<*>}
 */
export function debounce(func, wait = 300, options = { leading: false, trailing: true }) {
  return deb(func, wait, options)
}

/**
 * 根据文件url地址下载文件
 * @param url
 */
export function downloadByFileUrl(url) {
  // 创建一个隐藏的可下载链接
  const link = document.createElement('a')
  link.style.display = 'none'
  document.body.appendChild(link)

  // 设置链接的href属性为要下载的文件URL
  link.href = url

  // 设置下载的文件名（如果服务器没有设置Content-Disposition响应头的话）
  // link.download = 'desiredFileName.ext'

  // 触发点击事件
  link.click()

  // 清理
  document.body.removeChild(link)
}

/**
 * 路径拼接
 * @param path
 * @returns {*}
 */
export function connectPath(path) {
  return import.meta.env.VITE_APP_BASE_API + path
}

/**
 * 获取某年某月一共多少天
 * @param year
 * @param month
 * @returns {number}
 */
export function getDayByYearMonth(year, month) {
  return new Date(year, month, 0).getDate()
}

/**
 * 获取数组最后一项
 * @param arr
 * @returns {*}
 */
export function getArrayLastValue(arr) {
  if (Array.isArray(arr)) {
    return arr[arr.length - 1]
  } else {
    return arr
  }
}

/**
 * 查询当前树形节点的path
 * @param {[]} treeData
 * @param {number} targetId
 * @param {number[]} [path]
 * @returns {*[]|*|[]}
 */
export function getTreeNodeIds(treeData, targetId, path = []) {
  for (let node of treeData) {
    path.push(node.id)
    if (node.id === targetId) {
      return path
    }
    if (node.children && node.children.length > 0) {
      const foundPath = getTreeNodeIds(node.children, targetId, [...path])
      if (foundPath.length) {
        return foundPath
      }
    }
    path.pop()
  }
  return []
}

/**
 * 日期格式化
 * @param {Object | String | Number} time
 * @param {String} pattern
 * @returns {String}
 */
export function parseTime(time, pattern) {
  if (arguments.length === 0 || !time) {
    return null
  }
  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
  let date
  if (typeof time === 'object') {
    date = time
  } else {
    if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
      time = parseInt(time)
    } else if (typeof time === 'string') {
      time = time
        .replace(new RegExp(/-/gm), '/')
        .replace('T', ' ')
        .replace(new RegExp(/\.[\d]{3}/gm), '')
    }
    if (typeof time === 'number' && time.toString().length === 10) {
      time = time * 1000
    }
    date = new Date(time)
  }
  const formatObj = {
    y: date.getFullYear(),
    m: date.getMonth() + 1,
    d: date.getDate(),
    h: date.getHours(),
    i: date.getMinutes(),
    s: date.getSeconds(),
    a: date.getDay(),
  }
  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
    let value = formatObj[key]
    // 注意: getDay()在周日返回0
    if (key === 'a') {
      return ['日', '一', '二', '三', '四', '五', '六'][value]
    }
    if (result.length > 0 && value < 10) {
      value = '0' + value
    }
    return value || 0
  })
  return time_str
}

/**
 * 表单重置
 * @param {import("vue").Ref<String>} refName
 */
export function resetForm(refName) {
  if (this.$refs[refName]) {
    this.$refs[refName].resetFields()
  }
}

/**
 * 添加日期范围
 * @param {Object} params
 * @param {Array} dateRange
 * @param {String} propName
 * @returns {Object}
 */
export function addDateRange(params, dateRange, propName) {
  let search = params
  search.params =
    typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}
  dateRange = Array.isArray(dateRange) ? dateRange : []
  if (typeof propName === 'undefined') {
    search.params['beginTime'] = dateRange[0]
    search.params['endTime'] = dateRange[1]
  } else {
    search.params['begin' + propName] = dateRange[0]
    search.params['end' + propName] = dateRange[1]
  }
  return search
}

/**
 * 回显数据字典
 * @param {Object<T>} datas
 * @param {String} value
 * @returns {String}
 */
export function selectDictLabel(datas, value) {
  if (value === undefined) {
    return ''
  }
  var actions = []
  Object.keys(datas).some((key) => {
    if (datas[key].value === '' + value) {
      actions.push(datas[key].label)
      return true
    }
  })
  if (actions.length === 0) {
    actions.push(value)
  }
  return actions.join('')
}

/**
 * 回显数据字典（字符串数组）
 * @param {Object} datas
 * @param {String | Array} value
 * @param {String} separator
 * @returns {String}
 */
export function selectDictLabels(datas, value, separator) {
  if (value === undefined || value.length === 0) {
    return ''
  }
  if (Array.isArray(value)) {
    value = value.join(',')
  }
  var actions = []
  var currentSeparator = undefined === separator ? ',' : separator
  var temp = value.split(currentSeparator)
  Object.keys(value.split(currentSeparator)).some((val) => {
    var match = false
    Object.keys(datas).some((key) => {
      if (datas[key].value === '' + temp[val]) {
        actions.push(datas[key].label + currentSeparator)
        match = true
      }
    })
    if (!match) {
      actions.push(temp[val] + currentSeparator)
    }
  })
  return actions.join('').substring(0, actions.join('').length - 1)
}

/**
 * 字符串格式化(%s )
 * @param {String} str
 * @returns
 */
export function sprintf(str) {
  var args = arguments,
    flag = true,
    i = 1
  str = str.replace(/%s/g, function () {
    var arg = args[i++]
    if (typeof arg === 'undefined') {
      flag = false
      return ''
    }
    return arg
  })
  return flag ? str : ''
}

/**
 * 转换字符串，undefined,null等转化为""
 * @param {String} str
 * @returns {String}
 */
export function parseStrEmpty(str) {
  if (!str || String(str) === 'undefined' || String(str) === 'null') {
    return ''
  }
  return str
}

/**
 * 数据合并
 * @param {Object} source
 * @param {Object} target
 * @returns {Object}
 */
export function mergeRecursive(source, target) {
  for (var p in target) {
    try {
      if (target[p].constructor === Object) {
        source[p] = mergeRecursive(source[p], target[p])
      } else {
        source[p] = target[p]
      }
    } catch (e) {
      source[p] = target[p]
    }
  }
  return source
}

/**
 * 构造树型结构数据
 * @param {Object} data 数据源
 * @param {String} id id字段 默认 'id'
 * @param {String} parentId 父节点字段 默认 'parentId'
 * @param {String} children 孩子节点字段 默认 'children'
 */
export function handleTree(data, id, parentId, children) {
  let config = {
    id: id || 'id',
    parentId: parentId || 'parentId',
    childrenList: children || 'children',
  }

  var childrenListMap = {}
  var nodeIds = {}
  var tree = []

  for (let d of data) {
    let parentId = d[config.parentId]
    if (childrenListMap[parentId] == null) {
      childrenListMap[parentId] = []
    }
    nodeIds[d[config.id]] = d
    childrenListMap[parentId].push(d)
  }

  for (let d of data) {
    let parentId = d[config.parentId]
    if (nodeIds[parentId] == null) {
      tree.push(d)
    }
  }

  for (let t of tree) {
    adaptToChildrenList(t)
  }

  function adaptToChildrenList(o) {
    if (childrenListMap[o[config.id]] !== null) {
      o[config.childrenList] = childrenListMap[o[config.id]]
    }
    if (o[config.childrenList]) {
      for (let c of o[config.childrenList]) {
        adaptToChildrenList(c)
      }
    }
  }

  return tree
}

/**
 * 参数处理
 * @param {Object} params  参数
 * @returns {String}
 */
export function tansParams(params) {
  let result = ''
  for (const propName of Object.keys(params)) {
    const value = params[propName]
    const part = encodeURIComponent(propName) + '='
    if (value !== null && value !== '' && typeof value !== 'undefined') {
      if (typeof value === 'object') {
        for (const key of Object.keys(value)) {
          if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
            let params = propName + '[' + key + ']'
            var subPart = encodeURIComponent(params) + '='
            result += subPart + encodeURIComponent(value[key]) + '&'
          }
        }
      } else {
        result += part + encodeURIComponent(value) + '&'
      }
    }
  }
  return result
}

/**
 * 返回项目路径
 * @param {String} p
 * @returns {String}
 */
export function getNormalPath(p) {
  if (p.length === 0 || !p || String(p) === 'undefined') {
    return p
  }
  let res = p.replace('//', '/')
  if (res[res.length - 1] === '/') {
    return res.slice(0, res.length - 1)
  }
  return res
}

/**
 * 验证是否为blob格式
 * @param {Object} data
 * @returns {Boolean}
 */
export function blobValidate(data) {
  return data.type !== 'application/json'
}

/**
 * 经过打包处理的文件路径获取，支持"@/"别名 与 assets/...直接写的方式
 * @param {String} filePath - 相对于src的文件路径
 * @returns {String} - 完整的路径
 */
export function rocRequire(filePath) {
  let url = ''
  if (filePath.indexOf('@/') !== -1) {
    const newFilePath = filePath.replace('@/', '')
    url = new window.URL(`../${newFilePath}`, import.meta.url)
  } else {
    url = new window.URL(`../${filePath}`, import.meta.url)
  }
  return url.href
}

/**
 * 字典数据转换为对象形式
 * @param {Array[{}]} labelValue
 * @returns {Object}
 */
export function labelValueToKeyValue(labelValue) {
  let newLabelValue = []
  if (isRef(labelValue)) {
    newLabelValue = labelValue.value
  } else {
    newLabelValue = labelValue
  }
  const obj = {}
  if (Array.isArray(newLabelValue)) {
    newLabelValue.map((item) => {
      obj[item.value] = item.label
    })
  }
  return obj
}

/**
 * 字符串分割数组
 * 支持指定分隔符
 * 支持指定数组中值的类型
 * @param {String} str
 * @param {String} dot
 * @param {"String" | "Number" | "Boolean"} type
 * @returns {String[] | Number[] | Boolean[]}
 */
export function string2Array(str, dot = ',', type = 'String') {
  let arr
  if (str.trim()) {
    arr = str.split(dot)
  } else {
    arr = []
  }
  return arr.map((value) => {
    switch (type) {
      case 'String':
        return String(value)
      case 'Number':
        return Number(value)
      case 'Boolean':
        return Boolean(value)
      default:
        return value
    }
  })
}

/**
 * 两数运算 指定保留小数
 * 默认 相乘 保留3位小数
 * @param {Number | String} num1
 * @param {Number | String} num2
 * @param {Object} options
 * @param {Number | String} options.decimal
 * @param {"plus" | "minus" | "times" | "div"} options.type
 * @returns {Number}
 */
export function calc(num1, num2, options = { decimal: 3, type: 'times' }) {
  let _num1 = Number(num1)
  let _num2 = Number(num2)
  let _decimal = Number(options.decimal)
  const _type = options.type

  if (isNaN(_num1)) _num1 = 0
  if (isNaN(_num2)) _num2 = 0
  if (isNaN(_decimal)) _decimal = 0

  const num1BigNumber = new BigNumber(_num1)

  if (_type === 'plus') {
    return Number(num1BigNumber.plus(_num2).toFixed(_decimal))
  } else if (_type === 'minus') {
    return Number(num1BigNumber.minus(_num2).toFixed(_decimal))
  } else if (_type === 'times') {
    return Number(num1BigNumber.times(_num2).toFixed(_decimal))
  } else if (_type === 'div') {
    return Number(num1BigNumber.div(_num2).toFixed(_decimal))
  } else {
    return 0
  }
}

/**
 * 下载文件
 * @param {String} url - 地址
 * @param {String} fileName - 文件名
 * @param {"png" | "jpeg" | "gif" | "pdf"} type - 类型 默认 png
 */
export function downloadFile(url, fileName, type = 'png') {
  return new Promise((resolve, reject) => {
    let mime = ''
    if (type === 'png') {
      mime = 'image/png'
    } else if (type === 'jpeg') {
      mime = 'image/jpeg'
    } else if (type === 'gif') {
      mime = 'image/gif'
    } else if (type === 'pdf') {
      mime = `application/pdf`
    }
    let xhr = new XMLHttpRequest()
    xhr.open('get', url, true)
    xhr.setRequestHeader('Content-Type', mime)
    xhr.responseType = 'blob'
    xhr.onload = function () {
      if (this.status === 200) {
        const blob = this.response
        const blobUrl = window.URL.createObjectURL(blob)
        const a = document.createElement('a')
        a.href = blobUrl
        a.download = fileName
        a.click()
        window.URL.revokeObjectURL(blobUrl)
        resolve()
      } else {
        reject()
      }
    }
    xhr.send()
  })
}
